Skip to content

fix: 修正 MXU_WEBHOOK_ACTION 的 HTTP 請求處理#259

Closed
DoSerZBlock wants to merge 3 commits into
MistEO:mainfrom
DoSerZBlock:main
Closed

fix: 修正 MXU_WEBHOOK_ACTION 的 HTTP 請求處理#259
DoSerZBlock wants to merge 3 commits into
MistEO:mainfrom
DoSerZBlock:main

Conversation

@DoSerZBlock

@DoSerZBlock DoSerZBlock commented Jun 29, 2026

Copy link
Copy Markdown

摘要

這個 PR 修正 MXU_WEBHOOK_ACTION 目前只能送出 GET 請求、且非成功 HTTP 狀態仍被視為成功的問題。

原本 webhook action 只支援:

{
  "url": "https://example.com/webhook"
}

這對需要 POST JSON payload 的 webhook 服務不夠完整,例如 Discord Webhook。

主要變更

  • 保留舊版 { "url": "..." } 格式,預設仍以 GET 請求送出。

  • 新增支援:

    • method
    • headers
    • body
    • json
    • timeout_secs
    • fail_on_non_success
  • 當設定中包含 bodyjson,且沒有明確指定 method 時,預設改用 POST。

  • HTTP 回應狀態不是 2xx 時,預設回傳 false,避免 4xx / 5xx 被誤判為 webhook 執行成功。

  • log 不再輸出完整 webhook URL,避免 webhook token 被寫入 log。

使用範例

{
  "url": "https://discord.com/api/webhooks/xxx/yyy",
  "method": "POST",
  "headers": {
    "Content-Type": "application/json"
  },
  "body": {
    "content": "MXU 任務已完成"
  }
}

相容性

舊設定格式仍可正常使用,不需要修改既有的 interface.json 設定。

驗證

已執行:

cd src-tauri
cargo check

結果通過。

Summary by Sourcery

扩展 MXU webhook 自定义动作,以支持可配置的 HTTP 请求和更严格的成功判定标准。

新功能:

  • 允许在 MXU_WEBHOOK_ACTION 中通过动作参数指定 HTTP 方法、请求头、请求体或 JSON 负载、超时时间以及失败行为。

缺陷修复:

  • 默认将来自 MXU_WEBHOOK_ACTION 的非 2xx HTTP 响应视为失败,而不是始终报告成功。
  • 防止记录完整的 webhook URL,以避免在日志中泄露令牌。

增强改进:

  • 当存在 webhook 请求体或 JSON 负载时自动默认使用 POST,同时在无请求体的情况下保持 GET 为默认方法。
  • 为 webhook 配置引入验证和规范化处理,包括 URL 解析、方法和请求头验证以及超时时间限制。
Original summary in English

Summary by Sourcery

Extend MXU webhook custom action to support configurable HTTP requests and stricter success criteria.

New Features:

  • Allow MXU_WEBHOOK_ACTION to specify HTTP method, headers, body or JSON payload, timeout, and failure behavior via action parameters.

Bug Fixes:

  • Treat non-2xx HTTP responses from MXU_WEBHOOK_ACTION as failures by default instead of always reporting success.
  • Prevent logging of full webhook URLs to avoid exposing tokens in logs.

Enhancements:

  • Add automatic POST default when a webhook body or JSON payload is present while keeping GET as the default for body-less requests.
  • Introduce validation and normalization for webhook configuration, including URL parsing, method and header validation, and timeout clamping.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并且留下了一些高层次的反馈:

  • 当在 webhook 配置中同时提供 bodyjson 时,body.or(json) 会在没有提示的情况下优先选择 body;建议显式地校验并拒绝这种互相冲突的配置,以避免产生令人意外的行为。
  • 超时限制范围 clamp(1, 300) 有点像“魔法数字”;将这些限制提取成具名常量会让行为更清晰,也更方便后续调整。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- 当在 webhook 配置中同时提供 `body``json` 时,`body.or(json)` 会在没有提示的情况下优先选择 `body`;建议显式地校验并拒绝这种互相冲突的配置,以避免产生令人意外的行为。
- 超时限制范围 `clamp(1, 300)` 有点像“魔法数字”;将这些限制提取成具名常量会让行为更清晰,也更方便后续调整。

## Individual Comments

### Comment 1
<location path="src-tauri/src/mxu_actions.rs" line_range="420-429" />
<code_context>
     };

-    info!("[MXU_WEBHOOK] Sending GET request to: {}", url);
+    let body = body.or(json);
+    let method = match parse_webhook_method(method.as_deref(), body.is_some()) {
+        Ok(value) => value,
</code_context>
<issue_to_address>
**issue (bug_risk):** 使用 `json(&value)` 可能会覆盖用户提供的 `Content-Type` 头。

对于非字符串的请求体,`request.json(&value)` 总是会设置 `Content-Type: application/json`,这会覆盖在 `headers` 中设置的任何头。如果调用方必须控制内容类型,建议通过 `request.body(serde_json::to_vec(&value)?/to_string())` 手动序列化,以保留他们设置的头,或者仅在尚未设置 `Content-Type` 时才使用 `json()`。
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • When both body and json are provided in the webhook config, body.or(json) silently prefers body; consider explicitly validating and rejecting conflicting settings to avoid surprising behavior.
  • The timeout clamp range clamp(1, 300) is a bit of a magic number; extracting these limits into named constants would make the behavior clearer and easier to adjust later.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- When both `body` and `json` are provided in the webhook config, `body.or(json)` silently prefers `body`; consider explicitly validating and rejecting conflicting settings to avoid surprising behavior.
- The timeout clamp range `clamp(1, 300)` is a bit of a magic number; extracting these limits into named constants would make the behavior clearer and easier to adjust later.

## Individual Comments

### Comment 1
<location path="src-tauri/src/mxu_actions.rs" line_range="420-429" />
<code_context>
     };

-    info!("[MXU_WEBHOOK] Sending GET request to: {}", url);
+    let body = body.or(json);
+    let method = match parse_webhook_method(method.as_deref(), body.is_some()) {
+        Ok(value) => value,
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `json(&value)` may override user-provided `Content-Type` headers.

For non-string bodies, `request.json(&value)` always sets `Content-Type: application/json`, which will override any header set in `headers`. If callers must control the content type, consider serializing manually via `request.body(serde_json::to_vec(&value)?/to_string())` so their header is preserved, or only use `json()` when `Content-Type` is not already present.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +420 to +429
let body = body.or(json);
let method = match parse_webhook_method(method.as_deref(), body.is_some()) {
Ok(value) => value,
Err(e) => {
warn!("[MXU_WEBHOOK] {}", e);
return false;
}
};

let headers = match build_webhook_headers(headers) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): 使用 json(&value) 可能会覆盖用户提供的 Content-Type 头。

对于非字符串的请求体,request.json(&value) 总是会设置 Content-Type: application/json,这会覆盖在 headers 中设置的任何头。如果调用方必须控制内容类型,建议通过 request.body(serde_json::to_vec(&value)?/to_string()) 手动序列化,以保留他们设置的头,或者仅在尚未设置 Content-Type 时才使用 json()

Original comment in English

issue (bug_risk): Using json(&value) may override user-provided Content-Type headers.

For non-string bodies, request.json(&value) always sets Content-Type: application/json, which will override any header set in headers. If callers must control the content type, consider serializing manually via request.body(serde_json::to_vec(&value)?/to_string()) so their header is preserved, or only use json() when Content-Type is not already present.

@DoSerZBlock DoSerZBlock marked this pull request as draft June 29, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant